package order

import (
	"context"
	// "fmt"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/order/model"
	"mall/service/order/rpc/internal/svc"
	"mall/service/order/rpc/types/order"

	// "github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetOrderByIdLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetOrderByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetOrderByIdLogic {
	return &GetOrderByIdLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetOrderByIdLogic) GetOrderById(in *order.BaseIdReq) (*order.OrderInfo, error) {
	if (in.Filter == nil || len(*in.Filter) == 0) && (in.Id == nil || *in.Id == 0) {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.ParsePreloadAndEagerLoad(in.Preload, in.EagerLoad)

	// logrus.Info(fmt.Sprintf("GetOrderById preloadMap: %+v", preloadMap))
	// map[order:[Id OrderType] orderItem:[Id SkuId Quantity]]

	// logrus.Info(fmt.Sprintf("GetOrderById eagerLoadMap: %+v", eagerLoadMap))
	// map[order:[orderItem]]

	orderEntityName := model.RpcEntityNames.Order
	_, hasSettingMap := model.RpcEntityPreloadMap[orderEntityName]
	if !hasSettingMap {
		return &order.OrderInfo{}, nil
	}

	orderRepositoryName := model.RepositoryNames.Order
	columnMap, hasColumnMap := model.RepositoryPreloadMap[orderRepositoryName]
	if !hasColumnMap {
		return &order.OrderInfo{}, nil
	}

	var trade *model.Order
	if in.Filter != nil && len(*in.Filter) > 0 {
		var selectCols []string
		if preloadFields, hasPreloadFields := preloadMap[orderEntityName]; hasPreloadFields {
			selectCols = modelx.ParseSelectColumns(preloadFields, columnMap)
		}
		selectCols = append(selectCols, model.OrderColumns.TradeState)
		selectCols = append(selectCols, model.OrderColumns.UserID)
		selectCols = append(selectCols, model.OrderColumns.ID)

		filterMap := modelx.ParseFilter(in.Filter, columnMap)
		// logrus.Info(fmt.Sprintf("GetOrderById filterMap: %+v", filterMap))
		// map[ID:map[equalTo:[47]] trade_state:map[equalTo:[11]] user_id:map[equalTo:[10021]]]
		if filterMap == nil {
			return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
		}

		orderBy := modelx.ParseSorter(in.Sorter, columnMap)
		// logrus.Info(fmt.Sprintf("GetOrderById orderBy: %s", orderBy))
		//
		if len(orderBy) == 0 {
			orderBy = model.OrderColumns.ID + " DESC"
		}

		res, _, err := l.svcCtx.Order.GetList(l.ctx, model.M{
			"pageSize": uint64(1),
			"orderBy":  orderBy,
			"select":   strings.Join(selectCols, ","),
		}, -1, filterMap)
		if err != nil {
			return nil, err
		}

		if len(res) == 0 {
			return &order.OrderInfo{}, nil
		}

		trade = res[0]
	} else if in.Id != nil && *in.Id > 0 {
		data, err := l.svcCtx.Order.FindOne(l.ctx, *in.Id)
		if err != nil {
			return nil, err
		}

		trade = data
	}

	if trade == nil {
		return &order.OrderInfo{}, nil
	}

	if trade.TradeState == uint16(globalkey.OrderTradeStateUserDeleted) || trade.TradeState == uint16(globalkey.OrderTradeStateAdminDeleted) {
		return &order.OrderInfo{}, nil
	}

	if in.VisitorId != nil && *in.VisitorId != trade.UserID {
		return &order.OrderInfo{}, nil
	}

	oid := trade.ID
	subEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
	if eagerLoadEntities, hasEagerLoadEntities := eagerLoadMap[orderEntityName]; hasEagerLoadEntities {
		for _, entity := range eagerLoadEntities {
			if entitySettingMap, hasEntitySettingMap := model.RpcEntityPreloadMap[entity]; hasEntitySettingMap {
				subEntitySettingMap[entity] = entitySettingMap
			}
		}
	}

	orderSkuEntityName := model.RpcEntityNames.OrderItem
	orderSkuRepositoryName := model.RepositoryNames.OrderItem
	var skus []*order.OrderSkuInfo
	if _, hasEntitySettingMap := subEntitySettingMap[orderSkuEntityName]; hasEntitySettingMap {
		subPreloadFields, hasSubPreloadFields := preloadMap[orderSkuEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderSkuRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.OrderItemColumns.OrderID] = map[string][]any{
				"equalTo": []any{oid},
			}
			list, _, err := l.svcCtx.Order.GetItemList(l.ctx, model.M{
				"select": strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				for _, v := range list {
					skus = append(skus, &order.OrderSkuInfo{
						Id:             &v.OrderItemID,
						OrderId:        &v.OrderID,
						ProductCode:    &v.ProductCode,
						SkuId:          &v.SkuID,
						SkuCode:        &v.SkuCode,
						Sku:            &v.Sku,
						ImageId:        pointy.GetPointer(strconv.FormatInt(int64(v.SkuImageID), 10)),
						ImageUrl:       &v.SkuImageURL,
						ProductId:      &v.ProductID,
						EntryId:        &v.EntryID,
						Title:          &v.Title,
						Slug:           &v.Slug,
						ProductType:    pointy.GetPointer(uint32(v.ProductType)),
						BrandId:        &v.BrandID,
						BrandTitle:     &v.BrandTitle,
						BrandSlug:      &v.BrandSlug,
						GemPrice:       pointy.GetPointer(moneyx.FormatGem(int64(v.GemPrice))),
						RetailPrice:    pointy.GetPointer(moneyx.FormatAmount(int64(v.RetailPrice))),
						Quantity:       pointy.GetPointer(uint32(v.Quantity)),
						Weight:         pointy.GetPointer(moneyx.FormatWeight(int64(v.Weight))),
						TradeState:     pointy.GetPointer(uint32(v.TradeState)),
						Status:         pointy.GetPointer(uint32(v.Status)),
						GemAmount:      pointy.GetPointer(moneyx.FormatGem(int64(v.GemAmount))),
						RetailAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.RetailAmount))),
						DiscountAmount: pointy.GetPointer(moneyx.FormatAmount(int64(v.DiscountAmount))),
						AdjustAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.AdjustAmount))),
						ActualAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.ActualAmount))),
						RefundAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.RefundAmount))),
						RefundQuantity: pointy.GetPointer(uint32(v.RefundQuantity)),
					})
				}
			}
		}
	}

	orderAddressEntityName := model.RpcEntityNames.OrderAddress
	orderAddressRepositoryName := model.RepositoryNames.OrderAddress
	var addresses []*order.OrderAddressInfo
	if _, hasEntitySettingMap := subEntitySettingMap[orderAddressEntityName]; hasEntitySettingMap {
		subPreloadFields, hasSubPreloadFields := preloadMap[orderAddressEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderAddressRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.OrderAddressColumns.OrderID] = map[string][]any{
				"equalTo": []any{oid},
			}
			subFilterMap[model.OrderAddressColumns.Status] = map[string][]any{
				"equalTo": []any{globalkey.StatusEnabled},
			}
			list, _, err := l.svcCtx.Order.GetAddressList(l.ctx, model.M{
				"select": strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				for _, v := range list {
					addresses = append(addresses, &order.OrderAddressInfo{
						Id:         &v.OrderAddressID,
						OrderId:    &v.OrderID,
						UserId:     &v.UserID,
						Consignee:  &v.Consignee,
						Mobile:     &v.Mobile,
						Province:   &v.Province,
						City:       &v.City,
						District:   &v.District,
						Street:     &v.Street,
						Address:    &v.DetailedAddress,
						PostalCode: &v.PostalCode,
						RegionCode: &v.RegionCode,
						Label:      &v.Label,
						Status:     pointy.GetPointer(uint32(v.Status)),
					})
				}
			}
		}
	}

	orderAdjustmentEntityName := model.RpcEntityNames.OrderAdjustment
	orderAdjustmentRepositoryName := model.RepositoryNames.OrderAdjustment
	var adjustments []*order.OrderAdjustmentInfo
	if _, hasEntitySettingMap := subEntitySettingMap[orderAdjustmentEntityName]; hasEntitySettingMap {
		subPreloadFields, hasSubPreloadFields := preloadMap[orderAdjustmentEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderAdjustmentRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.OrderAdjustmentColumns.OrderID] = map[string][]any{
				"equalTo": []any{oid},
			}
			subFilterMap[model.OrderAdjustmentColumns.Status] = map[string][]any{
				"equalTo": []any{globalkey.StatusEnabled},
			}
			list, _, err := l.svcCtx.Order.GetAdjustmentList(l.ctx, model.M{
				"select": strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				for _, v := range list {
					adjustments = append(adjustments, &order.OrderAdjustmentInfo{
						Id:         &v.OrderAdjustmentID,
						OrderId:    &v.OrderID,
						Amount:     pointy.GetPointer(moneyx.FormatAmount(int64(v.Amount))),
						Memo:       &v.Memo,
						Status:     pointy.GetPointer(uint32(v.Status)),
						OperatedBy: &v.OperatedBy,
						OperatedAt: pointy.GetPointer(modelx.FormatNullDotTime(v.OperatedAt, "")),
					})
				}
			}
		}
	}

	orderDiscountEntityName := model.RpcEntityNames.OrderDiscount
	orderDiscountRepositoryName := model.RepositoryNames.OrderDiscount
	var discounts []*order.OrderDiscountInfo
	if _, hasEntitySettingMap := subEntitySettingMap[orderDiscountEntityName]; hasEntitySettingMap {
		subPreloadFields, hasSubPreloadFields := preloadMap[orderDiscountEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderDiscountRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.OrderDiscountColumns.OrderID] = map[string][]any{
				"equalTo": []any{oid},
			}
			list, _, err := l.svcCtx.Order.GetDiscountList(l.ctx, model.M{
				"select": strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				for _, v := range list {
					discounts = append(discounts, &order.OrderDiscountInfo{
						Id:               &v.OrderDiscountID,
						OrderId:          &v.OrderID,
						SkuId:            &v.SkuID,
						ProductId:        &v.ProductID,
						Title:            &v.Title,
						Slug:             &v.Slug,
						ActivityId:       &v.ActivityID,
						ActivityRuleId:   &v.ActivityRuleID,
						CouponTemplateId: &v.CouponTemplateID,
						CouponId:         &v.CouponID,
						GrouponAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(v.GrouponAmount))),
						WholesaleAmount:  pointy.GetPointer(moneyx.FormatAmount(int64(v.WholesaleAmount))),
						CouponAmount:     pointy.GetPointer(moneyx.FormatAmount(int64(v.CouponAmount))),
						ActivityAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.ActivityAmount))),
						IntegralAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.IntegralAmount))),
						DiscountAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.DiscountAmount))),
						Integrals:        &v.Integrals,
					})
				}
			}
		}
	}

	orderPaymentEntityName := model.RpcEntityNames.OrderPayment
	orderPaymentRepositoryName := model.RepositoryNames.OrderPayment
	var payments []*order.OrderPaymentInfo
	if _, hasEntitySettingMap := subEntitySettingMap[orderPaymentEntityName]; hasEntitySettingMap {
		subPreloadFields, hasSubPreloadFields := preloadMap[orderPaymentEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderPaymentRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.OrderPaymentColumns.OrderID] = map[string][]any{
				"equalTo": []any{oid},
			}
			subFilterMap[model.OrderPaymentColumns.Status] = map[string][]any{
				"equalTo": []any{globalkey.PaymentStatusPaid},
			}
			list, _, err := l.svcCtx.Order.GetPaymentList(l.ctx, model.M{
				"select":  strings.Join(subSelectCols, ","),
				"orderBy": model.OrderPaymentColumns.OrderPaymentID + " DESC",
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				for _, v := range list {
					payments = append(payments, &order.OrderPaymentInfo{
						Id:            pointy.GetPointer(v.OrderPaymentID),
						OrderId:       pointy.GetPointer(v.OrderID),
						No:            pointy.GetPointer(v.No),
						TransactionID: pointy.GetPointer(v.TransactionID),
						Amount:        pointy.GetPointer(moneyx.FormatAmount(int64(v.Amount))),
						Type:          pointy.GetPointer(uint32(v.Type)),
						Name:          pointy.GetPointer(v.Name),
						Code:          pointy.GetPointer(v.Code),
						Status:        pointy.GetPointer(uint32(v.Status)),
						PaidAt:        pointy.GetPointer(modelx.FormatNullDotTime(v.PaidAt, "")),
					})
				}
			}
		}
	}

	orderShipmentEntityName := model.RpcEntityNames.OrderShipment
	orderShipmentRepositoryName := model.RepositoryNames.OrderShipment
	var shipments []*order.OrderShipmentInfo
	if _, hasEntitySettingMap := subEntitySettingMap[orderShipmentEntityName]; hasEntitySettingMap {
		subPreloadFields, hasSubPreloadFields := preloadMap[orderShipmentEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderShipmentRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.OrderShipmentColumns.OrderID] = map[string][]any{
				"equalTo": []any{oid},
			}
			subFilterMap[model.OrderShipmentColumns.Status] = map[string][]any{
				"equalTo": []any{globalkey.StatusEnabled},
			}
			list, _, err := l.svcCtx.Order.GetShipmentList(l.ctx, model.M{
				"select": strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				for _, v := range list {
					shipments = append(shipments, &order.OrderShipmentInfo{
						Id:        &v.OrderShipmentID,
						OrderId:   &v.OrderID,
						No:        &v.No,
						Type:      pointy.GetPointer(uint32(v.Type)),
						Name:      &v.Name,
						Code:      &v.Code,
						Status:    pointy.GetPointer(uint32(v.Status)),
						UpdatedAt: pointy.GetPointer(modelx.FormatTime(v.UpdatedAt, "")),
					})
				}
			}
		}
	}

	return &order.OrderInfo{
		Id:              &trade.ID,
		ParentId:        &trade.ParentID,
		UserId:          &trade.UserID,
		UserRank:        &trade.UserRank,
		Buyer:           &trade.Buyer,
		Sn:              &trade.OrderSN,
		OrderType:       pointy.GetPointer(uint32(trade.OrderType)),
		SourceType:      pointy.GetPointer(uint32(trade.SourceType)),
		Number:          pointy.GetPointer(uint32(trade.Quantity)),
		GemAmount:       pointy.GetPointer(moneyx.FormatGem(int64(trade.GemAmount))),
		RetailAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(trade.RetailAmount))),
		Freight:         pointy.GetPointer(moneyx.FormatAmount(int64(trade.Freight))),
		DiscountAmount:  pointy.GetPointer(moneyx.FormatAmount(int64(trade.DiscountAmount))),
		AdjustAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(trade.AdjustAmount))),
		ActualAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(trade.ActualAmount))),
		DepositAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(trade.DepositAmount))),
		BalanceAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(trade.BalanceAmount))),
		RemainingAmount: pointy.GetPointer(moneyx.FormatAmount(int64(trade.RemainingAmount))),
		RefundAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(trade.RefundAmount))),
		Gems:            &trade.Gems,
		TradeState:      pointy.GetPointer(uint32(trade.TradeState)),
		Remark:          &trade.Remark,
		SellerMemo:      &trade.SellerMemo,
		CreatedAt:       pointy.GetPointer(modelx.FormatTime(trade.CreatedAt, "")),
		UpdatedAt:       pointy.GetPointer(modelx.FormatTime(trade.UpdatedAt, "")),
		DeletedAt:       pointy.GetPointer(modelx.FormatNullDotTime(trade.DeletedAt, "")),
		PaidAt:          pointy.GetPointer(modelx.FormatNullDotTime(trade.PaidAt, "")),
		ShippedAt:       pointy.GetPointer(modelx.FormatNullDotTime(trade.ShippedAt, "")),
		DeliveredAt:     pointy.GetPointer(modelx.FormatNullDotTime(trade.DeliveredAt, "")),
		ConfirmedAt:     pointy.GetPointer(modelx.FormatNullDotTime(trade.ConfirmedAt, "")),
		ReviewedAt:      pointy.GetPointer(modelx.FormatNullDotTime(trade.ReviewedAt, "")),
		FulfilledAt:     pointy.GetPointer(modelx.FormatNullDotTime(trade.FulfilledAt, "")),
		ClosedAt:        pointy.GetPointer(modelx.FormatNullDotTime(trade.ClosedAt, "")),
		AutoComfirmDays: pointy.GetPointer(uint32(trade.AutoComfirmDays)),
		Growth:          pointy.GetPointer(uint32(trade.Growth)),
		Integrals:       pointy.GetPointer(uint32(trade.Integrals)),
		Skus:            skus,
		Addresses:       addresses,
		Adjustments:     adjustments,
		Discounts:       discounts,
		Payments:        payments,
		Shipments:       shipments,
	}, nil
}
